using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Multiplicity.Runtime.Serialization.Binary;

namespace Multiplicity.Reflection {
    sealed class MetadataStreamHeader {

        readonly Version KnownVersion = new Version(2, 0);

        public readonly Version Version;
        public readonly byte HeapSizeMask;
        public readonly ulong ValidTableMask;
        public readonly ulong SortedTableMask;
        public readonly uint TableCount;
        public readonly uint[] RowCounts;

        internal MetadataStreamHeader(BinaryReader br) {
            br.ReadUInt32(); // reserved

            this.Version = new Version(br.ReadByte(), br.ReadByte());
            if (Version != KnownVersion) {
                MetadataException.ThrowUnknownVersion(Version);
            }

            this.HeapSizeMask = br.ReadByte();

            br.ReadByte(); // reserved

            this.ValidTableMask = br.ReadUInt64();
            this.TableCount = (uint)BinaryParser.BitCount(ValidTableMask);

            this.SortedTableMask = br.ReadUInt64();

            this.RowCounts = new uint[TableCount];
            for (int i = 0; i < TableCount; i++) {
                RowCounts[i] = br.ReadUInt32();
            }
        }

        public bool TableExists(MetadataTableType table) {
            return 0 != (ValidTableMask & (1ul << (int)table));
        }

        public uint TableRowCount(MetadataTableType table) {
            Throw.ArgumentOutOfRangeExceptionIf(!TableExists(table), "Table {0} does not exist", table);

            return RowCounts[TablePhysicalIndex(table)];
        }

        uint TablePhysicalIndex(MetadataTableType table) {
            Throw.ArgumentOutOfRangeExceptionIf(!TableExists(table), "Table {0} does not exist", table);

            uint i = 0;
            uint phy = 0;

            for (i = 0; i < TableCount && i < (uint)(table); i++) {
                if (TableExists((MetadataTableType)(i))) phy++;
            }

            return phy;
        }
    }
}
